package com.company.project.controller.ldjsc.InternetInformation;

import com.google.gson.Gson;
import okhttp3.HttpUrl;
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author xlliu24
 */
public class WebTtsWs {

//    @Value("${tts.api-xunfei.app-id}")
//    private static String APPID;
//    @Value("${tts.api-xunfei.api-key}")
//    private String APIKey;
//    @Value("${tts.api-xunfei.api-secret}")
//    private String APISecret;
//    @Value("${tts.api-xunfei.api-url}")
//    private String hostUrl;

    // 地址与鉴权信息
    public static final String hostUrl = "https://tts-api.xfyun.cn/v2/tts";
    // 均到控制台-语音合成页面获取
    public static final String APPID = "e2d4df72";
    public static final String APISecret = "ZTY0ODA3NTE3MmFhODc2NWRjZmU1Njlk";
    public static final String APIKey = "5a4e4886d58253187cd87fe28d7319e2";
    // 合成文本
    // 合成文本编码格式
    public static final String TTE = "UTF8"; // 小语种必须使用UNICODE编码作为值
    // 发音人参数。到控制台-我的应用-语音合成-添加试用或购买发音人，添加后即显示该发音人参数值，若试用未添加的发音人会报错11200
    public static final String VCN = "x4_lingfeizhe_zl";
    public static final String VCNNV = "x4_xiaoguo";
    // json
    public static final Gson gson = new Gson();


    public void generateSpeech(String text, String pcmPath, String wavPath, String voiceType) throws Exception {
        String wsUrl = getAuthUrl(hostUrl, APIKey, APISecret).replace("https://", "wss://");
        OutputStream outputStream = new FileOutputStream(pcmPath);
        boolean[] wsCloseFlag = {false};

        websocketWork(wsUrl, outputStream, text, wsCloseFlag, voiceType);

        // 等待WebSocket处理完成
        while (!wsCloseFlag[0]) {
            Thread.sleep(100);
        }

        // 将PCM转换为WAV
        pcmToWav(pcmPath, wavPath, 16000, 1, 16);
    }

    // 修改websocketWork方法以接收动态参数
    public static void websocketWork(String wsUrl, OutputStream outputStream, String text, boolean[] wsCloseFlag, String voiceType) {
        try {
            URI uri = new URI(wsUrl);
            WebSocketClient webSocketClient = new WebSocketClient(uri) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    System.out.println("ws建立连接成功...");
                }

                @Override
                public void onMessage(String message) {
                    JsonParse myJsonParse = gson.fromJson(message, JsonParse.class);
                    if (myJsonParse.code != 0) {
                        System.out.println("发生错误，错误码为：" + myJsonParse.code);
                        System.out.println("本次请求的sid为：" + myJsonParse.sid);
                    }
                    if (myJsonParse.data != null) {
                        try {
                            byte[] textBase64Decode = Base64.getDecoder().decode(myJsonParse.data.audio);
                            outputStream.write(textBase64Decode);
                            outputStream.flush();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if (myJsonParse.data.status == 2) {
                            try {
                                outputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            System.out.println("本次请求的sid==>" + myJsonParse.sid);
                            wsCloseFlag[0] = true;
                        }
                    }
                }

                @Override
                public void onClose(int i, String s, boolean b) {
                    System.out.println("ws链接已关闭，本次请求完成...");
                }

                @Override
                public void onError(Exception e) {
                    System.out.println("发生错误 " + e.getMessage());
                }
            };

            webSocketClient.connect();
            while (!webSocketClient.getReadyState().equals(org.java_websocket.enums.ReadyState.OPEN)) {
                Thread.sleep(100);
            }

            String gender = VCN;
            if (voiceType.equals("女")){
                gender = VCNNV;
            }

            // 发送请求参数
            String requestJson = "{\n" +
                    "  \"common\": {\n" +
                    "    \"app_id\": \"" + APPID + "\"\n" +
                    "  },\n" +
                    "  \"business\": {\n" +
                    "    \"aue\": \"raw\",\n" +
                    "    \"tte\": \"" + TTE + "\",\n" +
                    "    \"ent\": \"intp65_cn\",\n" +
                    "    \"vcn\": \"" + gender + "\",\n" +
                    "    \"pitch\": 50,\n" +
                    "    \"speed\": 55\n" +
                    "  },\n" +
                    "  \"data\": {\n" +
                    "    \"status\": 2,\n" +
                    "    \"text\": \"" + Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)) + "\"\n" +
                    "  }\n" +
                    "}";
            webSocketClient.send(requestJson);

            // 等待服务端返回完毕后关闭
            while (!wsCloseFlag[0]) {
                Thread.sleep(200);
            }
            webSocketClient.close();

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    // 鉴权方法
    public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        // 时间
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        // 拼接
        String preStr = "host: " + url.getHost() + "\n" +
                "date: " + date + "\n" +
                "GET " + url.getPath() + " HTTP/1.1";
        // SHA256加密
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        // Base64加密
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // 拼接
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        // 拼接地址
        HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder()
                .addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8)))
                .addQueryParameter("date", date)
                .addQueryParameter("host", url.getHost())
                .build();

        return httpUrl.toString();
    }

    /**
     * 将PCM文件转换为WAV文件
     * @param pcmFilePath PCM文件路径
     * @param wavFilePath WAV文件路径
     * @param sampleRate 采样率 (Hz)
     * @param channels 声道数 (1:单声道, 2:立体声)
     * @param bitsPerSample 采样位数 (8, 16, 24, 32)
     * @throws IOException
     */
    public static void pcmToWav(String pcmFilePath, String wavFilePath, int sampleRate, int channels, int bitsPerSample) throws IOException {
        File pcmFile = new File(pcmFilePath);
        File wavFile = new File(wavFilePath);

        try (FileInputStream fis = new FileInputStream(pcmFile);
             FileOutputStream fos = new FileOutputStream(wavFile)) {

            // 计算文件大小
            long pcmSize = pcmFile.length();
            long totalDataLen = pcmSize + 36; // 不包括前面的8字节
            long byteRate = sampleRate * channels * bitsPerSample / 8;

            // WAV文件头
            byte[] header = new byte[44];

            // RIFF chunk
            header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F';
            ByteBuffer.wrap(header, 4, 4).order(ByteOrder.LITTLE_ENDIAN).putInt((int) (pcmSize + 36));
            header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E';

            // fmt sub-chunk
            header[12] = 'f'; header[13] = 'm'; header[14] = 't'; header[15] = ' ';
            ByteBuffer.wrap(header, 16, 4).order(ByteOrder.LITTLE_ENDIAN).putInt(16); // Subchunk1Size
            ByteBuffer.wrap(header, 20, 2).order(ByteOrder.LITTLE_ENDIAN).putShort((short) 1); // PCM format
            ByteBuffer.wrap(header, 22, 2).order(ByteOrder.LITTLE_ENDIAN).putShort((short) channels);
            ByteBuffer.wrap(header, 24, 4).order(ByteOrder.LITTLE_ENDIAN).putInt(sampleRate);
            ByteBuffer.wrap(header, 28, 4).order(ByteOrder.LITTLE_ENDIAN).putInt((int) byteRate);
            ByteBuffer.wrap(header, 32, 2).order(ByteOrder.LITTLE_ENDIAN).putShort((short) (channels * bitsPerSample / 8)); // Block align
            ByteBuffer.wrap(header, 34, 2).order(ByteOrder.LITTLE_ENDIAN).putShort((short) bitsPerSample);

            // data sub-chunk
            header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a';
            ByteBuffer.wrap(header, 40, 4).order(ByteOrder.LITTLE_ENDIAN).putInt((int) pcmSize);

            // 写入WAV文件头
            fos.write(header);

            // 写入PCM数据
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }
    }

    // 返回的json结果拆解
    static class JsonParse {
        int code;
        String sid;
        Data data;
    }

    static class Data {
        int status;
        String audio;
    }
}
